/*
 * Copyright (c) 2006-2011, Petr Panteleyev
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 *    1. Redistributions of source code must retain the above copyright notice,
 *       this list of conditions and the following disclaimer.
 *    2. Redistributions in binary form must reproduce the above copyright notice,
 *       this list of conditions and the following disclaimer in the documentation
 *       and/or other materials provided with the distribution.
 *    3. The name of the author may not be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package org.panteleyev.gclient;

import com.google.gdata.util.ServiceException;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import com.google.gdata.client.GoogleService;
import com.google.gdata.client.Query;
import com.google.gdata.client.blogger.BloggerService;
import com.google.gdata.client.photos.PicasawebService;
import com.google.gdata.data.DateTime;
import com.google.gdata.data.Entry;
import com.google.gdata.data.Feed;
import com.google.gdata.data.media.MediaFileSource;
import com.google.gdata.data.photos.AlbumEntry;
import com.google.gdata.data.photos.AlbumFeed;
import com.google.gdata.data.photos.PhotoEntry;
import com.google.gdata.data.photos.UserFeed;
import com.google.gdata.util.ResourceNotFoundException;

/**
 *
 * @author Petr Panteleyev
 */
public class GBAccount {
    public  static final String FEED_URL = "http://www.blogger.com/feeds/";

    private static final String METAFEED_URL = FEED_URL + "default/blogs";
    private static final String USER_ALBUM_FEED_URL     = "http://picasaweb.google.com/data/feed/api/user/%s?kind=album&thumbsize=72u,94,110,144u,288,400,640,800,1024,1280";
    private static final String GET_PHOTOS_URL          = "http://picasaweb.google.com/data/feed/api/user/%s/albumid/%s/?kind=photo&thumbsize=72u,144u,288,400,640,800,1024,1280";
    private static final String USER_ALBUM_POST_URL     = "http://picasaweb.google.com/data/feed/api/user/%s";
    private static final String USER_PHOTO_UPLOAD_URL   = "http://picasaweb.google.com/data/feed/api/user/%s/albumid/%s";

    private BloggerService _blogService = new BloggerService("petrus-blogger");
    private PicasawebService _picasaService = new PicasawebService("petrus-blogger");

    private ArrayList<GBlog> _blogs = new ArrayList<>();
    private ArrayList<GAlbum> _albums = new ArrayList<>();

    private String userName;

    /**
     * Creates a new instance of GBAccount
     */
    public GBAccount() {
    }

    public boolean hasBlogs() {
        return !_blogs.isEmpty();
    }

    public ArrayList<GBlog> getBlogs() { return _blogs; }
    public GoogleService getBlogService() { return _blogService; }

    public GBlog getBlog(int index) {
        try {
            return _blogs.get(index);
        } catch (IndexOutOfBoundsException ex) {
            return null;
        }
    }

    public int getBlogIndex(GBlog blog) {
        return _blogs.indexOf(blog);
    }

    public ArrayList<GAlbum> getAlbums() { return _albums; }
    public GAlbum getAlbum(int index) { return _albums.get(index); }
    public PicasawebService getPicasaService() { return _picasaService; }

    public void login(String uName, String passwd) throws GBException {
        userName = uName;

        // Get the list of blogs
        try {
            URL feedUrl = new URL(METAFEED_URL);

            _blogService.setUserCredentials(userName, passwd);

            // Send the request and receive the response:
            Feed resultFeed = _blogService.getFeed(feedUrl, Feed.class);
            List<Entry> entries = resultFeed.getEntries();
            _blogs.ensureCapacity(entries.size());

            for (Entry e : entries) {
                GBlog blog = new GBlog();
                blog.parse(e);
                _blogs.add(blog);
            }
        } catch (IOException | ServiceException e) {
            throw new GBException(e);
        }

        // Get the list of albums
        try {
            _picasaService.setUserCredentials(uName, passwd);

            new UserFeed().declareExtensions(_picasaService.getExtensionProfile());

            URL feedUrl = new URL(String.format(USER_ALBUM_FEED_URL, userName));
            UserFeed userFeed = _picasaService.getFeed(feedUrl, UserFeed.class);

            List<AlbumEntry> entries = userFeed.getAlbumEntries();
            _albums.ensureCapacity(entries.size());

            for (AlbumEntry e : entries) {
                GAlbum album = new GAlbum(e);
                _albums.add(album);
            }
        } catch (IOException | ServiceException e) {
            throw new GBException(e);
        }
    }

    public void reloadAlbums() throws GBException {
        try {
            URL feedUrl = new URL(String.format(USER_ALBUM_FEED_URL, userName));
            UserFeed userFeed = _picasaService.getFeed(feedUrl, UserFeed.class);

            List<AlbumEntry> entries = userFeed.getAlbumEntries();
            _albums.clear();
            _albums.ensureCapacity(entries.size());

            for (AlbumEntry e : entries) {
                GAlbum album = new GAlbum(e);
                _albums.add(album);
            }
        } catch (IOException | ServiceException e) {
            throw new GBException(e);
        }
    }

    public void addAlbum(GAlbum album) throws GBException {
        try {
            URL postUrl = new URL(String.format(USER_ALBUM_POST_URL, userName));
            _picasaService.insert(postUrl, album.getEntry());
        } catch (IOException | ServiceException e) {
            throw new GBException(e);
        }
    }

    public void updateAlbum(GAlbum album) throws GBException {
        try {
            AlbumEntry updated = _picasaService.update(album.getEditURL(), album.getEntry());
            album.setEntry(updated);
        } catch (IOException | ServiceException e) {
            throw new GBException(e);
        }
    }

    // TODO: check if needed
    public GAlbum findAlbumByTitle(String title) {
        for (GAlbum album : _albums) {
            if (album.getTitle().equals(title)) {
                return album;
            }
        }

        return null;
    }

    public GPhoto uploadPhoto(GAlbum album, File file) throws GBException {
        try {
            MediaFileSource media = new MediaFileSource(file, "image/png"); // TODO: should we define mime type?
            URL feedUrl = new URL(String.format(USER_PHOTO_UPLOAD_URL, userName, album.getEntry().getGphotoId()));
            PhotoEntry res = _picasaService.insert(feedUrl, PhotoEntry.class, media);
            return new GPhoto(res);
        } catch (IOException | ServiceException e) {
            throw new GBException(e);
        }
    }

    public ArrayList<GBEntry> getEntries(GBlog blog, int maxRes, Date fromDate, Date toDate, boolean updateCommentsCount)
            throws GBException {
        try {
            URL feedUrl = new URL("http://www.blogger.com/feeds/" + blog.getID() + "/posts/default");
            Query query = new Query(feedUrl);
            query.setMaxResults(maxRes);

            if (fromDate != null) {
                query.setPublishedMin(new DateTime(fromDate));
            }

            if (toDate != null) {
                query.setPublishedMax(new DateTime(toDate));
            }

            Feed resultFeed = _blogService.query(query, Feed.class);

            List<Entry> list = resultFeed.getEntries();
            ArrayList<GBEntry> resList = new ArrayList<>(list.size());
            for (Entry entry : list) {
                GBEntry gbEntry = new GBEntry(blog, entry);

                if (updateCommentsCount && !gbEntry.isDraft()) {
                    int c = getCommentsCount(gbEntry);
                    gbEntry.setCommentsCount(c);
                }

                resList.add(gbEntry);
            }

            return resList;
        } catch (IOException | ServiceException | GBException e) {
            throw new GBException(e);
        }
    }

    public void createEntry(GBEntry entry) throws GBException {
        try {
            GBlog blog = entry.getBlog();
            if (blog == null) {
                throw new IllegalStateException();
            }

            Entry inserted = _blogService.insert(blog.getPostURL(), entry.getEntry());
            entry.setEntry(inserted);
        } catch (IllegalStateException | IOException | ServiceException e) {
            throw new GBException(e);
        }
    }

    public void deleteEntry(GBBaseEntry entry) throws GBException {
        try {
            _blogService.delete(entry.getEditURL());
        } catch (IOException | ServiceException e) {
            throw new GBException(e);
        }
    }

    public void updateEntry(GBEntry entry) throws GBException {
        try {
            Entry updated = _blogService.update(entry.getEditURL(), entry.getEntry());
            entry.setEntry(updated);
        } catch (IOException | ServiceException e) {
            throw new GBException(e);
        }
    }

    public void createComment(GBComment comment) throws GBException {
        if (comment == null) {
            throw new IllegalStateException();
        }

        GBEntry post = comment.getPost();

        try {
            URL feedUrl = new URL("http://www.blogger.com/feeds/" + post.getBlog().getID() + "/" + post.getId() + "/comments/default");
            _blogService.insert(feedUrl, comment.getEntry());
        } catch (IOException | ServiceException e) {
            throw new GBException(e);
        }
    }

    public ArrayList<GBComment> getComments(GBEntry entry) throws GBException {
        try {
            URL feedUrl = entry.getCommentsFeedURL();
            Query query = new Query(feedUrl);
            Feed resultFeed = _blogService.query(query, Feed.class);
            List<Entry> list = resultFeed.getEntries();
            ArrayList<GBComment> resList = new ArrayList<>(list.size());
            for (Entry cEntry : list) {
                resList.add(new GBComment(entry, cEntry));
            }

            return resList;
        } catch (ResourceNotFoundException e) {
            throw new GBCommentsNotEnabledException(e);
        } catch (Exception e) {
            throw new GBException(e);
        }
    }

    public int getCommentsCount(GBEntry entry) throws GBException {
        try {
            URL feedUrl = entry.getCommentsFeedURL();
            Query query = new Query(feedUrl);
            Feed resultFeed = _blogService.query(query, Feed.class);
            List<Entry> list = resultFeed.getEntries();
            return list.size();
        } catch (ResourceNotFoundException e) {
            throw new GBCommentsNotEnabledException(e);
        } catch (Exception e) {
            throw new GBException(e);
        }
    }

    public ArrayList<GPhoto> getPhotos(GAlbum album) throws GBException {
        try {
            URL feedUrl = new URL(String.format(GET_PHOTOS_URL, userName, album.getEntry().getGphotoId()));
            AlbumFeed resultFeed = _picasaService.getFeed(feedUrl, AlbumFeed.class);

            List<PhotoEntry> list = resultFeed.getPhotoEntries();
            ArrayList<GPhoto> resList = new ArrayList<>(list.size());

            for (PhotoEntry phEntry : list) {
                resList.add(new GPhoto(phEntry));
            }

            return resList;
        } catch (IOException | ServiceException e) {
            throw new GBException(e);
        }
    }
}
